package com.young.common.util;

import com.young.common.config.TraceInterceptor;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * http工具类
 *
 * @author imrookie
 * @date 2019/5/19
 */
public class HttpUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    public static final int CONNECT_TIME_OUT = 5000;//连接超时时间
    public static final int READ_TIME_OUT = 5000;//读取超时时间

    public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println(get("https://www.baidu.com"));
       /* Map<String,String> paramMap = new HashMap<String,String>();
        paramMap.put("head", "wjy");
        paramMap.put("user-agent", "123");
        System.out.println(postJson("http://localhost:8080/test", "321", paramMap, 10000, 10000));*/
    }

    /**
     * 传输文件流(批量)
     * @param url
     * @param files
     * @return
     */
    public static String postFiles(String url, HashMap<String, InputStream> files){
        HttpURLConnection httpCon = null;
        try {
            String BOUNDARY = "---------7d4a6d158c9"; // 定义数据分隔线
            URL connUrl  = new URL(url);
            httpCon = (HttpURLConnection) connUrl.openConnection();
            httpCon.setConnectTimeout(CONNECT_TIME_OUT);
            httpCon.setReadTimeout(READ_TIME_OUT);
            httpCon.setDoOutput(true);
            httpCon.setDoInput(true);
            httpCon.setRequestMethod(RequestMethod.POST.name());
            httpCon.setUseCaches(false);
            httpCon.setAllowUserInteraction(true);
            httpCon.setRequestProperty("Charset", CommonConstants.CHARSET);
            httpCon.setRequestProperty("X-Requested-With", "XMLHttpRequest");//模拟ajax请求
            httpCon.setRequestProperty("connection", "Keep-Alive");
            httpCon.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            httpCon.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);

            httpCon.connect();


            OutputStream out = new DataOutputStream(httpCon.getOutputStream());
            try {
                byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线
                Iterator iter = files.entrySet().iterator();
                int i=0;
                while (iter.hasNext()) {
                    i++;
                    Map.Entry entry = (Map.Entry) iter.next();
                    String key = (String) entry.getKey();
                    InputStream val = (InputStream) entry.getValue();
                    StringBuilder sb = new StringBuilder();
                    sb.append("--");
                    sb.append(BOUNDARY);
                    sb.append("\r\n");
                    sb.append("Content-Disposition: form-data;name=\"file" + i
                            + "\";filename=\"" + key + "\"\r\n");
                    sb.append("Content-Type:application/octet-stream\r\n\r\n");

                    byte[] data = sb.toString().getBytes();
                    out.write(data);
                    DataInputStream in = new DataInputStream(val);
                    int bytes = 0;
                    byte[] bufferOut = new byte[1024];
                    while ((bytes = in.read(bufferOut)) != -1) {
                        out.write(bufferOut, 0, bytes);
                    }
                    out.write("\r\n".getBytes()); // 多个文件时，二个文件之间加入这个
                    in.close();
                }
                out.write(end_data);
                out.flush();
                out.close();
            } finally {
                IOUtils.closeQuietly(out);
            }
            int rscode = httpCon.getResponseCode();
            if (rscode == 200)return readRequestData(httpCon.getInputStream());
            logger.error("HTTP请求没有正确返回,状态["+rscode+"]");
        }catch(Exception e){
            logger.error("HTTP请求异常",e);
        }finally {
            try{
                if (httpCon != null)httpCon.disconnect();
            }catch(Exception e){
                logger.error("关闭HTTP请求异常",e);
            }
        }
        return null;
    }

    /**
     *
     * @param url 请求地址
     * @param paramMap 请求参数,不需要做url encode处理,方法内部会处理
     * @return
     */
    public static String post(String url, Map<String, String> paramMap){
        String data = "";
        if (paramMap != null && !paramMap.isEmpty()) {
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                if (entry == null || entry.getValue() == null) {
                    continue;
                }
                try {
                    data += "&" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    logger.error("HTTP请求编码异常(未阻断):", e);
                }
            }
        }
        return http(url, StringUtils.isBlank(data) ? data : data.substring(1), null, RequestMethod.POST, ContentType.X_WWW_FORM_URLENCODED, CONNECT_TIME_OUT, READ_TIME_OUT);
    }

    /**
     * post请求, Content-Type为application/json
     * @param url 地址
     * @param data 参数对象, 转为json后放在请求体中传输
     * @return
     */
    public static String postJson(String url, Object data){
        return postJson(url, data instanceof String ? (String) data : JsonUtil.toJson(data), null, CONNECT_TIME_OUT);
    }

    /**
     * post请求, Content-Type为application/json
     * @param url 地址
     * @param data 参数对象, 转为json后放在请求体中传输
     * @param timeOutMills 超时时间
     * @return
     */
    public static String postJson(String url, Object data, int timeOutMills){
        return postJson(url, data instanceof String ? (String) data : JsonUtil.toJson(data), null, timeOutMills);
    }

    /**
     * xml格式的post请求
     * @param url url
     * @param xmlStr xml数据
     * @return
     */
    public static String postXml(String url, String xmlStr){
        return http(url, xmlStr, null, RequestMethod.POST, ContentType.APPLICATION_XML, CONNECT_TIME_OUT, READ_TIME_OUT);
    }

    /**
     * 发送文本的post请求
     * @param url 请求url
     * @param text 文本
     * @return
     */
    public static String postText(String url, String text) {
        return http(url, text, null, RequestMethod.POST, ContentType.TEXT_PLAIN, CONNECT_TIME_OUT, READ_TIME_OUT);
    }

    /**
     * get请求
     * @param url 请求地址
     * @return
     */
    public static String get(String url) {
        return http(url, null, null, RequestMethod.GET, ContentType.X_WWW_FORM_URLENCODED, CONNECT_TIME_OUT, READ_TIME_OUT);
    }

    /**
     * get请求
     * @param url 请求地址
     * @param timeOutMills 超时时间
     * @return
     */
    public static String get(String url, int timeOutMills) {
        return http(url, null, null, RequestMethod.GET, ContentType.X_WWW_FORM_URLENCODED
                , timeOutMills > 0 ? timeOutMills : CONNECT_TIME_OUT
                , timeOutMills > 0 ? timeOutMills : READ_TIME_OUT);
    }

    /**
     * 请求方法枚举
     */
    public static enum RequestMethod{
        POST, GET, PUT, DELETE, HEAD;
    }

    /**
     * content-type枚举
     */
    public static enum ContentType{
        TEXT_JSON("text/json"),
        TEXT_XML("text/xml"),
        APPLICATION_JSON("application/json"),
        APPLICATION_XML("application/xml"),
        TEXT_PLAIN("text/plain"),
        X_WWW_FORM_URLENCODED("application/x-www-form-urlencoded"),
        FORM_DATA("multipart/form-data");
        String value;
        ContentType(String val){
            this.value = val;
        }
    }

    /**
     * post请求, 默认Content-Type为application/json
     * @param url 地址
     * @param jsonData json格式请求参数
     * @param headers 请求头
     * @return
     */
    private static String postJson(String url, String jsonData, Map<String,String> headers, int timeOutMills) {
        return postJson(url, jsonData, headers, timeOutMills, timeOutMills);
    }


    /**
     * post请求, 默认Content-Type为application/json
     * @param url 地址
     * @param jsonData json格式请求参数
     * @param headers 请求头
     * @param connectTimeout 链接超时
     * @param readTimeOut 读取超时
     * @return
     */
    private static String postJson(String url, String jsonData, Map<String,String> headers, int connectTimeout, int readTimeOut){
        return http(url, jsonData, headers, RequestMethod.POST, ContentType.APPLICATION_JSON, connectTimeout, readTimeOut);
    }

    /**
     * http请求, 基础方法, 其余方法在此基础上扩展
     * @param url url
     * @param postData 数据
     * @param headers 请求头
     * @param requestMethod 请求方法
     * @param contentType content-type
     * @param connectTimeout 链接超时
     * @param readTimeOut 读取超时
     * @return
     */
    private static String http(String url, String postData, Map<String,String> headers, RequestMethod requestMethod, ContentType contentType, int connectTimeout, int readTimeOut){
        HttpURLConnection httpCon = null;
        BufferedWriter out = null;
        OutputStream os = null;
        OutputStreamWriter osw = null;
        try {
            URL connUrl  = new URL(url);
            httpCon = (HttpURLConnection) connUrl.openConnection();
            httpCon.setConnectTimeout(connectTimeout);//连接超时
            httpCon.setReadTimeout(readTimeOut);//读取超时
            httpCon.setDoOutput(true);
            httpCon.setDoInput(true);
            httpCon.setRequestMethod(requestMethod.name());//请求方式
            httpCon.setUseCaches(false);
            httpCon.setAllowUserInteraction(true);
            httpCon.setRequestProperty("Content-Type", contentType.value + "; charset=UTF-8");
            httpCon.setRequestProperty("User-Agent", "young");
            httpCon.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01");
            if(headers!=null){
                for(Map.Entry<String,String> entry : headers.entrySet()){
                    httpCon.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }
            //接入追踪id
            String traceId = TraceInterceptor.getTraceId();
            httpCon.setRequestProperty(CommonConstants.TRACE_KEY, StringUtils.isNotBlank(traceId) ? traceId : CommonUtil.getUUID());

            httpCon.connect();

            if (StringUtils.isNotBlank(postData)) {
                os = httpCon.getOutputStream();
                osw = new OutputStreamWriter(os, "UTF-8");
                out = new BufferedWriter(osw);
                out.write(postData);
                out.flush();
            }

            int rscode = httpCon.getResponseCode();
            if (rscode == 200) {
                return readRequestData(httpCon.getInputStream());
            }
            logger.error("HTTP请求没有正确返回,状态[{}],url={},参数={},Content-Type={}", rscode, url, postData, contentType.value);
        }catch(Exception e){
            logger.error("HTTP请求异常:", e);
        }finally {
            try{
                if (out != null) {
                    out.close();
                }
                if (osw != null) {
                    osw.close();
                }
                if (os != null) {
                    os.close();
                }
                if (httpCon != null) {
                    httpCon.disconnect();
                }
            }catch(Exception e){
                logger.error("关闭HTTP请求异常:", e);
            }
        }
        return null;
    }

    /**
     * 读取输入流
     * @param is
     * @return
     */
    public static String readRequestData(InputStream is){
        StringBuffer sb = new StringBuffer();
        BufferedReader br =null;
        InputStreamReader isr = null;
        try {
            isr = new InputStreamReader(is, "UTF-8");
            br = new BufferedReader(isr);
            String str = null;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            return sb.toString();
        } catch (Exception ioe) {
            logger.error("读取HTTP请求的返回消息异常:", ioe);
        }finally{
            try {
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                logger.error("HTTP 关闭响应流异常:", e);
            }
        }
        return null;
    }
}
